﻿using OpenCvSharp.Dnn;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net;
using System.Drawing;
using System.Reflection.Emit;

namespace Console
{
    public class Detector
    {

        static string ROOT_DIR = "E:\\Jack\\Documents\\Project\\python\\opencv\\obj_detect\\yolov4-tiny";

        string CONFIGURATION_PATH = ROOT_DIR + "\\" + "yolov4-tiny.cfg";
        string WEIGHTS_PATH = ROOT_DIR + "\\" + "yolov4-tiny.weights";
        string CLASSES_PATH = ROOT_DIR + "\\" + "coco.names";

        List<string> class_names;

        float CON_THRES = 0.4f;
        float NMS_THRES = 0.6f;

        int INFER_WIDTH = 416;
        int INFER_HEIGHT = 416;

        Net net;
        VideoCapture video_capture;

        double VIDEO_FPS;
        int FRAME_COUNT;
        int FRAME_WIDTH;
        int FRAME_HEIGHT;

        int DP_WIDTH = 960;
        int DP_HEIGHT = 540;

        List<string> out_layers;
        OpenCvSharp.Size size;

        public Detector()
        {
            if (!read_classes())
            {
                return;
            }
            net = CvDnn.ReadNetFromDarknet(CONFIGURATION_PATH, WEIGHTS_PATH);
        }

        public Boolean read_classes()
        {
            StreamReader sr = null;
            string line;

            sr = new StreamReader(CLASSES_PATH);
            class_names = new List<string>();
            while ((line = sr.ReadLine()) != null)
            {
                class_names.Add(line);
            }
            return true;
        }

        public void setup()
        {
            List<string> layer_names;

            List<int> unconnected_out_layers;

            try
            {
                layer_names = net.GetLayerNames().ToList();
                out_layers = new List<string>();
                unconnected_out_layers = net.GetUnconnectedOutLayers().ToList();
                for (int i = 0; i < unconnected_out_layers.Count; i++)
                {
                    out_layers.Add(layer_names[unconnected_out_layers[i] - 1]);
                }
                size = new OpenCvSharp.Size(INFER_WIDTH, INFER_HEIGHT);
            }
            catch (Exception ex)
            {
                System.Console.WriteLine(ex);
            }
        }

        public Mat detect(Mat original_frame)
        {
            Mat[] outs;
            //Mat outs;
            Mat blob_image;
            Mat frame;

            //outs = new List<Mat>();
            outs = out_layers.Select(_ => new Mat()).ToArray();
            blob_image = CvDnn.BlobFromImage(original_frame, 1.0F / 255.0F, size);
            net.SetInput(blob_image);
            net.Forward(outs, out_layers);
            //outs = net.Forward();

            //DataFrame data_frame = post_process(original_frame, outs);
            frame = post_process(original_frame, outs);

            //command = "";
            //info = data_frame.getInfo();
            //resized_frame = data_frame.getFrame();
            //data_frame = new DataFrame(command, info, resized_frame);
            return frame;

        }

        public Mat post_process(Mat frame, Mat[] results)
        {
            Mat result;

            List<Rect2d> boxes;
            List<float> confidences;
            List<int> class_ids;

            //Mat boxes_mat;
            //MatOfFloat confidences_mat;
            //MatOfInt indices_mat;

            Mat resized_frame;
            OpenCvSharp.Size new_size;

            //string command, info;
            //DataFrame data_frame;
            try
            {
                //System.out.println("后处理");
                boxes = new List<Rect2d>();
                class_ids = new List<int>();
                confidences = new List<float>();

                for (int i = 0; i < results.Length; i++)
                {
                    result = results[i];
                    for (int j = 0; j < result.Rows; j++)
                    {
                        float max = 0;
                        int index = 0;
                        for (int k = 5; k < result.Cols; k++)
                        {
                            if (result.At<float>(j, k) > max)
                            {
                                max = result.At<float>(j, k);
                                index = k;
                            }

                        }

                        float confidence = max;
                        int class_id = index - 5;

                        if (confidence > CON_THRES)
                        {
                            float center_x = result.At<float>(j, 0) * DP_WIDTH;
                            float center_y = result.At<float>(j, 1) * DP_HEIGHT;
                            float width = result.At<float>(j, 2) * DP_WIDTH;
                            float height = result.At<float>(j, 3) * DP_HEIGHT;
                            float left = center_x - width / 2;
                            float top = center_y - height / 2;
                            boxes.Add(new Rect2d(left, top, width, height));
                            confidences.Add(confidence);
                            class_ids.Add(class_id);
                        }
                    }
                }

                //boxes_mat = new Mat();
                //boxes_mat.fromList(boxes);
                //confidences_mat = new MatOfFloat();
                //confidences_mat.fromList(confidences);
                //indices_mat = new MatOfInt();
                int[] indices = new int[boxes.Count];
                CvDnn.NMSBoxes(boxes, confidences, CON_THRES, NMS_THRES, out indices);

                resized_frame = new Mat();
                new_size = new OpenCvSharp.Size(DP_WIDTH, DP_HEIGHT);
                Cv2.Resize(frame, resized_frame, new_size);

                //int[] indices = indices_mat.toArray();
                for (int i = 0; i < indices.Length; i++)
                {
                    int index = indices[i];
                    Rect2d box = boxes[index];
                    double left = box.X;
                    double top = box.Y;
                    double width = box.Width;
                    double height = box.Height;
                    float confidence = confidences[index];
                    int class_id = class_ids[index];
                    string label = class_names[class_ids[i]] + " " + confidences[i];
                    //Console.WriteLine(label);
                    OpenCvSharp.Point p1 = new OpenCvSharp.Point(left, top);
                    OpenCvSharp.Point p2 = new OpenCvSharp.Point(left + width, top + height);
                    Cv2.Rectangle(resized_frame, p1, p2, new Scalar(255, 0, 0), 2);
                    Cv2.PutText(resized_frame, label, p1, HersheyFonts.Italic, 1, new Scalar(255, 0, 0), 2);
                    //System.out.println(box);
                }
                //command = "";
                //info = indices.Length.ToString();
                //data_frame = new DataFrame(command, info, resized_frame);
                //return data_frame;
                return resized_frame;
            }
            catch (Exception ex)
            {
                return null;
            }
        }





    }
}
